package com.clp.protocol.iec104.apdu.apci;

import com.clp.protocol.core.pdu.nbytepdu.BaseNBytePduClip;;
import com.clp.protocol.iec104.definition.ApduType;
import com.clp.protocol.iec104.definition.ConstVal;
import com.clp.protocol.core.pdu.ByteToStringFormat;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import lombok.*;

import java.util.Objects;
import java.util.function.Consumer;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
abstract class Apci<A extends Apci<A, C>, C extends CtrlArea<C>> extends BaseNBytePduClip<Apci<A, C>> {
    /**
     * 启动字符，固定值,1个字节
     */
    protected byte head = ConstVal.HEAD_VAL;

    /**
     * 长度字段，1字节
     */
    protected int length;

    /**
     * 控制域
     */
    protected C ctrlArea;

    @SuppressWarnings("unchecked")
    private A self() {
        return (A) this;
    }

    @Override
    @SuppressWarnings("unchecked")
    public A refreshFrom(ByteBuf buf) {
        // 1、设置启动字符
        this.head = buf.readByte();
        // 2、设置长度
        this.length = buf.readByte() & 0xFF;
        // 3、设置控制域
        switch (ApduType.gain(ByteBufUtil.getBytes(buf, buf.readerIndex(), ConstVal.CTRL_AREA_LEN))) {
            case UType:
                this.ctrlArea = (C) new UCtrlArea();
                break;
            case SType:
                this.ctrlArea = (C) new SCtrlArea();
                break;
            case IType:
                this.ctrlArea = (C) new ICtrlArea();
                break;
        }
        ctrlArea.refreshFrom(buf);
        return self();
    }

    @Override
    public boolean isValid() {
        if (head != ConstVal.HEAD_VAL) return false;
        return ctrlArea.isValid();
    }

    @Override
    public void writeBytesTo(ByteBuf buf) {
        // 1、设置启动字符
        buf.writeByte(head);
        // 2、设置长度
        buf.writeByte(length);
        // 3、设置控制域
        ctrlArea.writeBytesTo(buf);
    }

    @Override
    public void writeFormattedByteStringsTo(StringBuilder sb, String frameClipBytesSeparator, String byteSeparator, ByteToStringFormat byteFormat) {
        // 启动字符
        sb.append(byteFormat.format(head));
        sb.append(byteSeparator);
        // 长度字段
        if (length == 0) {
            sb.append("-");
        } else {
            sb.append(byteFormat.format((byte) length));
        }
        sb.append(frameClipBytesSeparator);
        ctrlArea.writeFormattedByteStringsTo(sb, frameClipBytesSeparator, byteSeparator, byteFormat);
    }

    @Override
    public void writeSimpleDescriptionTo(StringBuilder sb) {
        sb.append("[ ");
        ctrlArea.writeSimpleDescriptionTo(sb);
        sb.append(" ]\n");
    }

    @Override
    public void writeDetailDescriptionTo(StringBuilder sb) {
        sb.append("[ ");
        ctrlArea.writeDetailDescriptionTo(sb);
        sb.append(" ]\n");
    }

    @Override
    protected void forEachOneLevelChild(Consumer<BaseNBytePduClip<?>> consumer) {
        consumer.accept(ctrlArea);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Apci<?, ?> apci = (Apci<?, ?>) o;
        return head == apci.head && Objects.equals(ctrlArea, apci.ctrlArea);
    }

    @Override
    public int hashCode() {
        return Objects.hash(head, ctrlArea);
    }
}
